package courier

import (
	"bufio"
	"bytes"
	"errors"
	"fmt"
	"gitee.com/dongmingchao/decent-ft/src/caretaker"
	resourcePool "gitee.com/dongmingchao/decent-ft/src/resource-pool"
	"gitee.com/dongmingchao/decent-ft/src/utils"
	"github.com/libp2p/go-reuseport"
	"log"
	"net"
	"time"
)

func bridgeListenFunc(reader *bufio.Reader, addr net.Addr) ([]byte, error) {
	askCode, err := reader.ReadByte()
	//fmt.Println("TCP remote addr: ", addr, askCode)
	if err != nil {
		return nil, err
	}
	var buf bytes.Buffer
	switch Op(askCode) {
	case AskIndex:
		buf.WriteByte(byte(Done))
		caretaker.GlobalStash.Write(&buf)
	case Pass:
		fmt.Println("bridge recv pass", addr.String())
		parsePass(reader, addr)
	case Search: // 下潜搜索
		fmt.Println("Bridge Recv Search", addr)
		srcAddr, word, originAddr, _, jumpCount, originNei := parseSearch(reader, addr)
		f := caretaker.GlobalStash.SearchFileByNameExactly(word)
		srcNei, otherNeighbors := getSrcNeighborInBridges(srcAddr)
		if srcNei == nil {
			log.Fatalln("Bridge Last Source Neighbor Not Found!")
		}
		if f == nil {
			// 文件在本地没有找见
			var apped bytes.Buffer
			if jumpCount == 0 {
				srcNei.RemoteAddr.Write(&apped)
			} else {
				originAddr.Write(&apped)
			}
			apped.WriteByte(jumpCount)
			searchPassJumps(reader, jumpCount, apped)
			outer := bufio.NewReader(&apped)
			BridgeSearchFileContent(word, outer, otherNeighbors)
		} else {
			// 文件在本地找到了
			fmt.Println("file found:", word)
			file := caretaker.ReadStashFile(*f)
			searchBack(originNei, srcNei, originAddr, jumpCount, reader, file)
		}
	case Get:
		var originNei *resourcePool.GNeighbor
		originAddr := utils.NewUDPAddrFromAddr(addr)
		for i, neighbor := range caretaker.GlobalStash.Neighbors {
			if neighbor.RemoteAddr.IP.Equal(originAddr.IP) {
				originNei = &caretaker.GlobalStash.Neighbors[i]
			}
		}
		if originNei == nil {
			return nil, errors.New("bridge recv Get: Neighbor nil Error")
		}
		fmt.Println("UDP will be feed back ip: ", originNei.RemoteAddr)
		var hash [20]byte
		_, err = reader.Read(hash[:])
		if err == nil {
			mark := fmt.Sprintf("%x", hash)
			file := caretaker.ReadStashFileByMark(mark)
			var sent bytes.Buffer
			sent.WriteByte(byte(file.Len()))
			file.WriteTo(&sent)
			BridgeLocalCall(*originNei, func(_ net.Conn) []byte {
				return sent.Bytes()
			})
		}
	}
	return buf.Bytes(), nil
}

func handleConnection(c *net.TCPConn) {
	fmt.Printf("Serving %s\n", c.RemoteAddr().String())
	byPassAddr, _ := net.ResolveTCPAddr("tcp", c.RemoteAddr().String())
	neis := caretaker.GlobalStash.Neighbors
	lastNeighbor := &neis[len(neis)-1]
	if byPassAddr.IP.Equal(lastNeighbor.RemoteAddr.IP) {
		fmt.Println("same subnet")
		lastNeighbor.BridgeAddr = utils.NewUDPAddrFromAddr(c.RemoteAddr())
	} else {
		// TODO: 临时使用最新添加的neighbor作为bypass对象，高并发时逻辑有问题
		lastNeighbor.Bypass = c.RemoteAddr().String()
	}
	bridges[c.RemoteAddr().String()] = *c
	for {
		reader := bufio.NewReader(c)
		buf, err := bridgeListenFunc(reader, c.RemoteAddr())
		if err == nil {
			if len(buf) <= 0 {
				continue
			}
			var fin bytes.Buffer
			fin.Write(finalResponse(buf))
			n, err := fin.WriteTo(c)
			if err != nil {
				log.Fatalln("TCP send error")
			}
			fmt.Println("TCP send length: ", n)
			fmt.Println("TCP send content: ", fin)
		} else {
			log.Println("bridgeListenFunc error", err)
			break
		}
	}
	c.Close()
}

func BridgeLocalCall(srcNei resourcePool.GNeighbor, data func(net.Conn) []byte) {
	var sent bytes.Buffer
	sent.WriteByte(byte(Pass))
	// TODO: 最大下潜深度1byte = 255
	sent.WriteByte(byte(len(srcNei.DivePass)))
	for _, divePass := range srcNei.DivePass {
		sent.WriteString(divePass)
		sent.WriteByte(0)
	}
	BridgeSilentSend(srcNei, func(conn net.Conn) []byte {
		fmt.Println("Pass", conn.LocalAddr(), "->", conn.RemoteAddr())
		sent.Write(data(conn))
		return sent.Bytes()
	})
}

func BridgeSilentSend(srcNei resourcePool.GNeighbor, data func(net.Conn) []byte) {
	if srcNei.Bypass == "" {
		SilentSend(*srcNei.RemoteAddr, func(conn *net.UDPConn) []byte {
			return data(conn)
		})
	} else {
		conn := bridges[srcNei.Bypass]
		n, err := conn.Write(data(&conn))
		if err == nil {
			fmt.Println("BridgeSilentSend: length", n)
		} else {
			log.Fatalln("BridgeSilentSend: write error", err)
		}
	}
}

func BridgeSearchFileContent(word string, pass *bufio.Reader, neighbors []resourcePool.GNeighbor) *bytes.Buffer {
	var neiRange []resourcePool.GNeighbor
	if neighbors == nil {
		neiRange = caretaker.GlobalStash.Neighbors
	} else {
		neiRange = neighbors
	}
	var sent bytes.Buffer
	sent.WriteByte(byte(Search))
	sent.WriteString(word)
	sent.WriteByte(0)
	for _, neighbor := range neiRange {
		BridgeSilentSend(neighbor, func(conn net.Conn) []byte {
			lAddr := utils.NewUDPAddrFromAddr(conn.LocalAddr())
			lAddr.Port = poolAddr.Port
			if pass == nil {
				fmt.Println("origin addr", lAddr.String(), "->", neighbor.RemoteAddr.String())
				lAddr.Write(&sent)
				lAddr.Write(&sent)
				sent.WriteByte(0)
			} else {
				lAddr.Write(&sent)
				_, err := pass.WriteTo(&sent)
				if err != nil {
					log.Fatalln("BridgeSearchFileContent: read pass jump error")
				}
				//fmt.Println("final output sent", sent.Bytes())
			}
			return sent.Bytes()
		})
	}
	if pass == nil {
		passClosed = false
		passMsg = make(chan *bufio.Reader)
		ok := false
		var reader *bufio.Reader
		go func() {
			time.Sleep(3 * time.Second)
			if !ok {
				passMsg <- nil
			}
		}()
		reader, ok = <-passMsg
		close(passMsg)
		passClosed = true
		if reader == nil {
			return nil
		}
		length, err := reader.ReadByte()
		if err != nil {
			log.Fatalln("read get file length error")
		}
		buf := make([]byte, length)
		reader.Read(buf)
		return bytes.NewBuffer(buf)
	}
	return nil
}

func BridgeGetFileContent(key string, hash [20]byte) *bytes.Buffer {
	passClosed = false
	passMsg = make(chan *bufio.Reader)
	conn := bridges[key]
	var sent bytes.Buffer
	sent.WriteByte(byte(Get))
	sent.Write(hash[:])

	n, err := sent.WriteTo(&conn)
	if err == nil {
		fmt.Println("BridgeGet: send length:", n)
	} else {
		log.Fatalln("BridgeGet: send error")
	}
	reader := <-passMsg
	length, err := reader.ReadByte()
	if err != nil {
		log.Fatalln("read get file length error")
	}
	buf := make([]byte, length)
	reader.Read(buf)
	close(passMsg)
	passClosed = true
	return bytes.NewBuffer(buf)
}

var bridgeAddr *net.TCPAddr
var bridges map[string]net.TCPConn

func BridgeListen(addr *net.TCPAddr) {
	l, err := reuseport.Listen("tcp", addr.String())

	if err != nil {
		fmt.Println(err)
		return
	}
	defer l.Close()
	fmt.Println("start tcp bridge ", l.Addr())
	bridges = make(map[string]net.TCPConn)
	bridgeAddr, err = net.ResolveTCPAddr("tcp", l.Addr().String())
	if err != nil {
		log.Fatalln("bridge addr resolve error")
	}
	for {
		c, err := l.Accept()
		if err != nil {
			fmt.Println(err)
			return
		}
		go handleConnection(c.(*net.TCPConn))
	}
}
